#include <LiveWirer.hpp>

using namespace cv;

OCVLiveWire::OCVLiveWire()
{
    this->clean();
    initParams();
}

OCVLiveWire::OCVLiveWire(const cv::Mat &img, bool modeDebug)
{
    initParams();
    setDebugMode(modeDebug);
    loadImage(img);
}

/**
 * @brief   加载处理图像
 * @param  img        待处理图像      
 * @param  modeDebug        
 */
void OCVLiveWire::loadImage(const cv::Mat &img, bool modeDebug)
{
    setDebugMode(modeDebug);
    imgf = calcLiveWireCostFcn(img);
    isStartPath = false;
    isLoaded = true;
}

/**
 * @brief  初始化参数
 */
void OCVLiveWire::initParams()
{
    isLoaded = false;
    isStartPath = false;
    parRadiusA = 4;
    parRadiusP = 120;
    parRadiusPath = 300;
}

bool OCVLiveWire::isLoadedData() const
{
    return isLoaded;
}

bool OCVLiveWire::isStartedPath() const
{
    return isStartPath;
}

void OCVLiveWire::setDebugMode(bool mode)
{
    isDebug = mode;
}

cv::Point OCVLiveWire::getCPoint() const
{
    return cPoint;
}

cv::Point OCVLiveWire::getMPoint() const
{
    return mPoint;
}

void OCVLiveWire::clean()
{
    this->isLoaded = false;
    imgf.release();
    iPX.release();
    iPY.release();
}

/**
 * @brief  计算p值
 * @param  p                
 * @param  isAnchorIdeal    
 */
void OCVLiveWire::calcLWP(cv::Point p, bool isAnchorIdeal)
{
    if (isLoaded)
    {
        cv::Point tp = p;
        if (isAnchorIdeal)
        {
            tp = calcIdealAnchor(imgf, p, parRadiusA);
        }
        calcLiveWireP(imgf, tp.x, tp.y, iPX, iPY, parRadiusP);
        isStartPath = true;
        cPoint = tp;
    }
}

/**
 * @brief  计算LW路径
 * @param  p                
 * @param  isAnchorIdeal    
 */
void OCVLiveWire::calcLWPath(const cv::Point &p, bool isAnchorIdeal)
{
    if (isLoaded && isStartPath)
    {
        cv::Point tp = p;
        if (isAnchorIdeal)
        {
            tp = calcIdealAnchor(imgf, p, parRadiusA);
        }
        double pDST = cv::norm(tp - cPoint);
        bool isNoPath = true;
        if ((pDST > 1.0) && (pDST < parRadiusPath))
        {
            calcLiveWireGetPath(iPX, iPY, tp, path);
            if (path.size() > 0)
            {
                isNoPath = false;
            }
        }
        if (isNoPath)
        {
            path.clear();
            path.push_back(tp);
        }
        this->mPoint = tp;
    }
}

void OCVLiveWire::incPath()
{
    if (isLoaded)
    {
        if (path.size() > 0)
        {
            pathTot.insert(pathTot.end(), path.begin(), path.end());
        }
    }
}

void OCVLiveWire::helpDrawImgPXY(const std::string &winName)
{
    if (isLoaded)
    {
        cv::Mat tmp;
        cv::hconcat(iPX, iPY, tmp);
        cv::imshow(winName, normImage(tmp));
    }
}

void OCVLiveWire::helpDrawImgF(const std::string &winName)
{
    if (isLoaded)
    {
        cv::imshow(winName, normImage(imgf));
    }
}


void CreateLWMask(cv::Mat& src, const std::vector<cv::Point>& lwPath, cv::Mat& mask)
{
    mask = Mat::zeros(src.size(), CV_8UC1);
    int minX = INT_MAX, maxX = INT_MIN;
    int minY = INT_MAX, maxY = INT_MIN;

    for (auto p : lwPath)
    {
        if (minX > p.x)
            minX = p.x;
        if (maxX < p.x)
            maxX = p.x;
        if (minY > p.y)
            minY = p.y;
        if (maxY < p.y)
            maxY = p.y;
    }
    for (int i = 0; i < mask.rows; i++)
    {
        uchar* maskpix = mask.ptr<uchar>(i);
        for (int j = 0; j < mask.cols; j++)
        {
            Point pixpoint = Point(j, i);
            if (pixpoint.x < minX || pixpoint.x > maxX || pixpoint.y < minY || pixpoint.y > maxY)
            {
                maskpix++;
            }
            else
            {
                int count = 0;
                bool minXFlag = false, maxXFlag = false;
                bool minYFlag = false, maxYFlag = false;
                bool flag = false;
                for (auto p : lwPath)
                {
                    if (pixpoint.x > p.x && pixpoint.y > p.y && !minXFlag)
                    {
                        minXFlag = true;
                        count += 1;
                    }
                    if (pixpoint.x < p.x && pixpoint.y > p.y && !maxXFlag)
                    {
                        maxXFlag = true;
                        count += 1;
                    }
                    if (pixpoint.x < p.x && pixpoint.y < p.y && !minYFlag)
                    {
                        minYFlag = true;
                        count += 1;
                    }
                    if (pixpoint.x > p.x && pixpoint.y < p.y && !maxYFlag)
                    {
                        maxYFlag = true;
                        count += 1;
                    }
                }
                if (count == 4)
                {
                    *maskpix = 255;
                }
                maskpix++;
            }
        }
    }
}
